import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as qiniu from 'qiniu';
import { extname } from 'path';

export interface UploadResult {
  key: string;
  url: string;
  hash?: string;
}

@Injectable()
export class UploaderService {
  private readonly accessKey: string;
  private readonly secretKey: string;
  private readonly bucket: string;
  private readonly region: string;
  private readonly publicDomain?: string;

  constructor(private readonly config: ConfigService) {
    this.accessKey = this.config.get<string>('QINIU_AK', '');
    this.secretKey = this.config.get<string>('QINIU_SK', '');
    this.bucket = this.config.get<string>('QINIU_BUCKET', '');
    this.region = this.config.get<string>('QINIU_REGION', 'z0');
    this.publicDomain = this.config.get<string>('QINIU_PUBLIC_DOMAIN');
  }

  private createMac() {
    return new qiniu.auth.digest.Mac(this.accessKey, this.secretKey);
  }

  private createConfig() {
    const config = new qiniu.conf.Config();
    config.useHttpsDomain = true;
    const map: Record<string, any> = {
      z0: qiniu.zone.Zone_z0,
      z1: qiniu.zone.Zone_z1,
      z2: qiniu.zone.Zone_z2,
      na0: qiniu.zone.Zone_na0,
      as0: qiniu.zone.Zone_as0,
    };
    if (map[this.region]) config.zone = map[this.region];
    return config;
  }

  private uploadToken(scopeKey?: string) {
    const policy = new qiniu.rs.PutPolicy({ scope: scopeKey ? `${this.bucket}:${scopeKey}` : this.bucket });
    return policy.uploadToken(this.createMac());
  }

  private publicUrl(key: string) {
    if (!this.publicDomain) return '';
    const bucketManager = new qiniu.rs.BucketManager(this.createMac(), this.createConfig());
    return bucketManager.publicDownloadUrl(this.publicDomain, key);
  }

  private buildKey(originalname: string) {
    const ext = extname(originalname) || '';
    const now = new Date();
    const yyyy = now.getFullYear();
    const mm = String(now.getMonth() + 1).padStart(2, '0');
    const dd = String(now.getDate()).padStart(2, '0');
    const uuid = cryptoRandom();
    return `blog/${yyyy}-${mm}-${dd}-${uuid}${ext}`;
  }

  async uploadBuffer(file: Express.Multer.File): Promise<UploadResult> {
    try {
      const key = this.buildKey(file.originalname || 'file');
      const token = this.uploadToken(key);
      const formUploader = new qiniu.form_up.FormUploader(this.createConfig());
      const putExtra = new qiniu.form_up.PutExtra();
      (putExtra as any).mimeType = file.mimetype;
      const { data, resp } = await formUploader.put(token, key, file.buffer, putExtra);
      if (resp.statusCode !== 200) {
        throw new Error(`Qiniu upload failed: ${resp.statusCode} ${JSON.stringify(data)}`);
      }
      return { key: data.key ?? key, url: this.publicUrl(key), hash: data.hash };
    } catch (e: any) {
      throw new InternalServerErrorException(e?.message || 'Upload failed');
    }
  }
}

function cryptoRandom(): string {
  // lightweight random string
  return Math.random().toString(36).slice(2, 10) + Math.random().toString(36).slice(2, 10);
}


